package avriServer.server;


import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;

import avriServer.channel.Channel;
import avriServer.channel.NonexistantChannelException;
import avriServer.channel.TemporaryChannel;
import avriServer.channel.WrongChannelNameException;
import avriServer.client.Waiter;

public class ListChannel {
	private HashMap<String, Channel> channels = new HashMap<String, Channel>();
		
	/**
	 * _channelName can be the path of a channel such as : general/myChannel/channel1 
	 * In this case, it will directly return the channel called "channel1".
	 * It's also can be a simple name such as : general
	 * @param _channel
	 * @return the channel with path = _channelPath
	 * @throws NonexistantChannelException 
	 * @throws WrongChannelNameException 
	 */
	public Channel getChannel (String _channelNameOrAbsoluteName) throws NonexistantChannelException {
        if (_channelNameOrAbsoluteName == null)
			throw new NonexistantChannelException("Channel name is null.");
		int indexOfSeparator = _channelNameOrAbsoluteName.indexOf("/");
		if (indexOfSeparator == -1) {
			return this.getSpecificChannel(_channelNameOrAbsoluteName) ;
		}
		else {
			//if _channelNameOrAbsoluteName is the path of the channel : general/myChannel/channel1 
			//Channel subChannel = getSpecificChannel("general");
			Channel subChannel = getSpecificChannel(_channelNameOrAbsoluteName.substring(0, indexOfSeparator));
			
			//if _channelNameOrAbsoluteName is the path of the channel : general/myChannel/channel1 
			//tempChannel = Channel.create("myChannel/channel1");
            Channel tempChannel;
			try {
				tempChannel = new Channel(_channelNameOrAbsoluteName.substring(indexOfSeparator + 1, _channelNameOrAbsoluteName.length()));
			} catch (WrongChannelNameException e) {
				throw new NonexistantChannelException(_channelNameOrAbsoluteName) ;
			}
			return subChannel.getChannel(tempChannel.getAbsoluteName());
		}
		
	}
		
	/**
	 * get the channel with name _channelName.
	 * does the same thing than channels.get but only at the first level throw an UnexistantChannelException when the channel is unexistant
	 * @param _channelName
	 * @return
	 * @throws NonexistantChannelException if the channel is unexistant.
	 */
	private Channel getSpecificChannel (String _channelName) throws NonexistantChannelException {
		Channel theChannel = this.channels.get(_channelName);
		if (theChannel != null)
			return theChannel ;
		else
			throw new NonexistantChannelException(_channelName);
	}
	
	/**
	 * add a channel to the list.
	 * Automatically set the new absoluteName of the channel.
	 * Called by the Channel object into myChannel.addChannel method
	 * @param _absoluteNameParentChannel
	 * @param _channel
	 */
	public void addChannel (String _absoluteNameParentChannel, Channel _channel) {
		_channel.setAbsoluteName(_absoluteNameParentChannel+"/"+_channel.toString());
		this.channels.put(_channel.getName(), _channel);
	}

	/**
	 * add a channel to the list.
	 * Should only be used for first-level channel
	 * @param _absoluteNameParentChannel
	 * @param _channel
	 */
	public void addChannel (Channel _channel) {
		this.channels.put(_channel.getName(), _channel);
	}

	/**
	 * add a channel into THE RIGHT CHANNEL ! CHECK the _channel.absolutePath !.
	 * The _channel absoluteName IS NOT changed (it's the difference with addChannel (String _absoluteNameParentChannel, Channel _channel).
	 * Should be only called by the Dispatcher object into Dispatcher.addChannel('/general/myChannel/channel1') method
	 * @param _channel
	 * @throws NonexistantChannelException 
	 */
	/*
	void addChannel (Channel _channel) throws NonexistantChannelException {
		int indexOfSeparator = _channel.getAbsoluteName().lastIndexOf("/") ;
		//if it's a first-level channel, we directly add it into the list.
		if (indexOfSeparator == -1) {
			_channel.setParentChannel(null);
			this.channels.put(_channel.getName(), _channel);
		} 
		//if it's not a first-level, we get the parentChannel.
		//e.g: if _channel.getAbsoluteName() == general/myChannel/channel1
		//	   we select the channel general/myChannel/
		//	   we set _channel.parentChannel = general/myChannel/
		//     we add the channel _channel to the general/myChannel/ channel
		else {
			Channel channelParent = null ;
            channelParent = this.findParentChannel(_channel.getAbsoluteName());
			_channel.setParentChannel(channelParent);
			channelParent.addChannel(_channel);
		}
	}*/
	
	
	/**
	 * remove the channel named _channel.getName of this list.
	 * Also update all the waiters and delete his reference to the channel _channel.
	 * @param _channel
	 * @throws NonexistantChannelException 
	 */
	public void removeChannel (String _channelName) throws NonexistantChannelException {		
		Channel theChannel = this.channels.get(_channelName); 
		for (Entry<String, Waiter> entry : theChannel.getWaiters().entrySet()) {
			entry.getValue().unsubscribe(theChannel);
		}
		this.channels.remove(_channelName);
	}
	

	/**
	 * Remove the channels which implements the TemporaryChannels interface from the channels list if there is no waiter inside.
	 */
	public void cleanVacantChannel () {
		Iterator<Channel> it = this.channels.values().iterator();
		while(it.hasNext()) {
			Channel channel = it.next();
			channel.cleanVacantChannel();
			if (channel instanceof TemporaryChannel) {
				if (channel.getNumberOfWaiter() == 0) {
					it.remove();
				}
			}
		}
	}
	
	//we get the parent Channel: if we want to move the channel general/myChannel/channel1 to private/myPrivateChannel/channel1,
	//we have to select general/myChannel, remove channel1 and add it to private/myPrivateChannel/channel1

}
